﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace WebChatApi.Common
{
    /// <summary>
    /// Base Binary Manager
    /// File Structure:
    /// RootSharedFolder/Category/Index1/Index2/File
    /// </summary>
    public class BinaryManager
    {
        #region Fields

        /// <summary>
        /// The base virtual directory
        /// </summary>
        protected string baseVirtualDirectory = null;

        /// <summary>
        /// The base physical directory
        /// </summary>
        protected string basePhysicalDirectory = null;

        /// <summary>
        /// The category
        /// </summary>
        protected string category = string.Empty;

        #endregion

        #region Property

        /// <summary>
        /// Base physical path for current container.
        /// If it has specified category, the path is for category container.
        /// </summary>
        public string PhysicalPath
        {
            get
            {
                return baseVirtualDirectory;
            }
        }

        /// <summary>
        /// Base virtial path for current container 
        /// If it has specified category, the path is for category container.
        /// </summary>
        public string VirtualPath
        {
            get
            {
                return basePhysicalDirectory;
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryManager"/> class.
        /// </summary>
        /// <param name="baseVirtualDirectory">The base virtual directory.</param>
        /// <param name="basePhysicalDirectory">The base physical directory.</param>
        /// <param name="category">The category.</param>
        protected BinaryManager(string baseVirtualDirectory, string basePhysicalDirectory, string category = null)
        {
            this.baseVirtualDirectory = baseVirtualDirectory;
            this.basePhysicalDirectory = basePhysicalDirectory;
            this.category = category;

            Initialize();
        }

        #endregion



        /// <summary>
        /// Gets the physical path.
        /// </summary>
        /// <param name="binaryKey">The binary key.</param>
        /// <param name="extension">The extension.</param>
        /// <returns>The full physical path.</returns>
        public string GetPhysicalPath(Guid binaryKey, string extension = null)
        {
            return GetPath(this.basePhysicalDirectory, binaryKey, extension).Replace('/', '\\'); ;
        }

        /// <summary>
        /// Gets the virtual path.
        /// </summary>
        /// <param name="binaryKey">The binary key.</param>
        /// <param name="extension">The extension.</param>
        /// <returns>The full virtual path.</returns>
        public string GetVirtualPath(Guid binaryKey, string extension = null)
        {
            return GetPath(this.baseVirtualDirectory, binaryKey, extension).Replace('\\', '/'); ;
        }

        #region Protected methods

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        protected virtual void Initialize()
        {
            if (string.IsNullOrWhiteSpace(this.baseVirtualDirectory))
            {
                this.baseVirtualDirectory = "BinaryLibrary";
            }

            if (string.IsNullOrWhiteSpace(this.basePhysicalDirectory))
            {
                this.basePhysicalDirectory = System.Web.HttpContext.Current.Server.MapPath(this.baseVirtualDirectory);
            }

            if (!string.IsNullOrWhiteSpace(this.category))
            {
                this.baseVirtualDirectory = Path.Combine(this.baseVirtualDirectory, this.category);
                this.basePhysicalDirectory = Path.Combine(this.basePhysicalDirectory, this.category);
            }

            if (!Directory.Exists(this.basePhysicalDirectory))
            {
                Directory.CreateDirectory(this.basePhysicalDirectory);
            }
        }

        /// <summary>
        /// Gets the path.
        /// </summary>
        /// <param name="basePath">The base path.</param>
        /// <param name="binaryKey">The binary key.</param>
        /// <param name="extension">The extension.</param>
        /// <returns>The full path based on specific binary <see cref="Guid"/> key.</returns>
        protected string GetPath(string basePath, Guid binaryKey, string extension = null)
        {
            return GetPath(basePath, GenerateRelativePath(binaryKey, extension));
        }

        /// <summary>
        /// Gets the path.
        /// </summary>
        /// <param name="basePath">The base path.</param>
        /// <param name="relativePath">The relative path.</param>
        /// <returns>The full path.</returns>
        protected string GetPath(string basePath, string relativePath)
        {
            return Path.Combine(basePath, relativePath);
        }

        /// <summary>
        /// Generates the relative path.
        /// </summary>
        /// <param name="binaryKey">The binary key.</param>
        /// <param name="extension">The extension.(Optional)</param>
        /// <returns>The relative path.</returns>
        protected string GenerateRelativePath(Guid binaryKey, string extension = null)
        {
            string path = Path.Combine(GenerateIndexPath(binaryKey), binaryKey.ToString());

            if (!string.IsNullOrWhiteSpace(extension))
            {
                path += "." + extension;
            }

            return path;
        }

        #endregion

        #region Static public methods

        /// <summary>
        /// Reads the binary stream.
        /// </summary>
        /// <param name="physicalFullPath">The physical full path.</param>
        /// <returns>
        /// The <see cref="FileStream" /> instance for binary at specific path.
        /// </returns>
        /// <exception cref="InvalidObjectException">physicalFullPath</exception>
        /// <exception cref="OperationFailureException">ReadBinaryStreamByFilePath</exception>
        protected static FileStream ReadBinaryStreamByFilePath(string physicalFullPath)
        {
            FileStream result = null;

            try
            {
                if (!string.IsNullOrWhiteSpace(physicalFullPath) && File.Exists(physicalFullPath))
                {
                    result = new FileStream(physicalFullPath, FileMode.Open);
                }
                else
                {
                    throw new InvalidObjectException("physicalFullPath");
                }
            }
            catch (Exception ex)
            {
                throw new OperationFailureException("ReadBinaryStreamByFilePath", ex, physicalFullPath);
            }

            return result;
        }

        /// <summary>
        /// Reads the binary bytes by specific file path.
        /// </summary>
        /// <param name="physicalFullPath">The physical full path.</param>
        /// <returns>
        /// The <see cref="Byte" />  array of binary.
        /// </returns>
        /// <exception cref="OperationFailureException">ReadBinaryBytes</exception>
        protected static byte[] ReadBinaryBytesByFilePath(string physicalFullPath)
        {
            FileStream stream = ReadBinaryStreamByFilePath(physicalFullPath);
            return stream.ToBytes();
        }

        /// <summary>
        /// Reads the binary as text by file path.
        /// </summary>
        /// <param name="physicalFullPath">The physical full path.</param>
        /// <returns></returns>
        /// <exception cref="OperationFailureException">ReadBinaryAsTextByFilePath</exception>
        protected static string ReadBinaryAsTextByFilePath(string physicalFullPath)
        {
            try
            {
                return File.ReadAllText(physicalFullPath, Encoding.UTF8);
            }
            catch (Exception ex)
            {
                throw new OperationFailureException("ReadBinaryAsTextByFilePath", ex, physicalFullPath);
            }
        }

        /// <summary>
        /// Saves the binary.
        /// </summary>
        /// <param name="physicalFullPath">The physical full path.</param>
        /// <param name="bytes">The bytes.</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        /// <exception cref="OperationFailureException">SaveBinary</exception>
        protected static void SaveBinary(string physicalFullPath, byte[] bytes, bool overwrite = false)
        {
            try
            {
                if (overwrite || !File.Exists(physicalFullPath))
                {
                    File.WriteAllBytes(physicalFullPath, bytes);
                }
            }
            catch (Exception ex)
            {
                throw new OperationFailureException("SaveBinary", ex, physicalFullPath);
            }
        }

        /// <summary>
        /// Saves the text as binary.
        /// </summary>
        /// <param name="physicalFullPath">The physical full path.</param>
        /// <param name="content">The content.</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        /// <exception cref="OperationFailureException">SaveBinary</exception>
        protected static void SaveTextAsBinary(string physicalFullPath, string content, bool overwrite = false)
        {
            try
            {
                if (overwrite || !File.Exists(physicalFullPath))
                {
                    File.WriteAllText(physicalFullPath, content, Encoding.UTF8);
                }
            }
            catch (Exception ex)
            {
                throw new OperationFailureException("SaveTextAsBinary", ex, physicalFullPath);
            }
        }

        /// <summary>
        /// Generates the index path.
        /// </summary>
        /// <param name="binaryKey">The binary key.</param>
        /// <returns>
        /// The relative path for specific key.
        /// </returns>
        protected static string GenerateIndexPath(Guid binaryKey)
        {
            string guid = binaryKey.ToString();
            string path = guid.Substring(0, 2);
            path = Path.Combine(path, guid.Substring(0, 4));
            path = Path.Combine(path, guid.Substring(0, 6));
            path = Path.Combine(path, guid.Substring(0, 8));

            return path;
        }

        #endregion
    }


}
